package pers.vic.sso.server.service;

import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import pers.vic.boot.base.model.BaseResponse;
import pers.vic.boot.base.vo.BooleanWithMsg;
import pers.vic.sso.common.enums.GrantTypeEnum;
import pers.vic.sso.common.model.AccessTokenContent;
import pers.vic.sso.common.model.AuthorizationCode;
import pers.vic.sso.common.model.RpcAccessToken;
import pers.vic.sso.common.model.SsoUser;
import pers.vic.sso.server.config.SsoConfiguration;
import pers.vic.sso.server.session.AccessTokenManager;
import pers.vic.sso.server.session.CodeManager;
import pers.vic.sso.server.session.RefreshTokenManager;
import pers.vic.sso.server.session.TicketGrantingTicketManager;

import javax.annotation.Resource;

/**
 * 描述:
 * oauth2  service
 *
 * @author Vic.xu
 * @date 2021-11-03 8:33
 */
@Service
public class Oauth2Service {

    @Resource
    private CodeManager codeManager;

    @Resource
    private TicketGrantingTicketManager ticketGrantingTicketManager;

    @Resource
    private SsoUserService userService;


    @Resource
    private AccessTokenManager accessTokenManager;

    @Resource
    private RefreshTokenManager refreshTokenManager;

    @Resource
    private SsoConfiguration ssoConfiguration;


    /**
     * validate  and generate  AccessToken
     *
     * @param grantType
     * @param code
     * @param username
     * @param password
     * @param appId
     * @return
     */
    public BaseResponse<RpcAccessToken> validateAndGenerateToken(String grantType, String code, String username, String password,
                                                                 String appId) {
        BaseResponse<AccessTokenContent> accessTokenContentBaseResponse = validateAuth(grantType, code, username, password, appId);
        if (!accessTokenContentBaseResponse.isSuccess()) {
            return BaseResponse.error(accessTokenContentBaseResponse.getMsg());
        }

        RpcAccessToken token = generateRpcAccessToken(accessTokenContentBaseResponse.getData(), null);
        return BaseResponse.success(token);
    }

    /**
     * 校验授权码
     *
     * @param grantType
     * @param code
     * @param username
     * @param password
     * @param appId
     * @return
     */
    private BaseResponse<AccessTokenContent> validateAuth(String grantType, String code, String username, String password,
                                                          String appId) {
        AccessTokenContent authDto = null;
        if (GrantTypeEnum.AUTHORIZATION_CODE.name().equals(grantType)) {
            AuthorizationCode authorizationCode = codeManager.validateAndRemove(code);
            if (authorizationCode == null) {
                return BaseResponse.error("code不存在或已经过期");
            }

            SsoUser user = ticketGrantingTicketManager.getAndRefresh(authorizationCode.getTgt());
            if (user == null) {
                return BaseResponse.error("服务端session已过期");
            }
            authDto = new AccessTokenContent(authorizationCode, user, appId);
        } else if (GrantTypeEnum.PASSWORD.name().equals(grantType)) {
            // app通过此方式由客户端代理转发http请求到服务端获取accessToken
            BaseResponse<SsoUser> loginResult = userService.login(username, password);
            if (!loginResult.isSuccess()) {
                return BaseResponse.error(loginResult.getMsg());
            }
            SsoUser user = loginResult.getData();
            String tgt = ticketGrantingTicketManager.generate(loginResult.getData());
            AuthorizationCode codeContent = new AuthorizationCode(tgt, false, null);
            authDto = new AccessTokenContent(codeContent, user, appId);
        }
        return BaseResponse.success(authDto);
    }


    /**
     * 生成  accessToken;  accessTokende 的过期时间是然后生成refreshToken的一半<br />
     *      第一次验证的时候,accessToken 为空，则根据code相关信息生成新的accessToken；<br />
     *      后续来验证的时候，如果accessToken没有过期 则延续原来的accessToken；<br />
     *      然后生成refreshToken 一起返回给客户端<br />
     * @param accessTokenContent
     * @param accessToken
     * @return
     */
    public RpcAccessToken generateRpcAccessToken(AccessTokenContent accessTokenContent, String accessToken) {
        if (accessToken == null || !accessTokenManager.refresh(accessToken)) {
            accessToken = accessTokenManager.generate(accessTokenContent);
        }

        String refreshToken = refreshTokenManager.generate(accessTokenContent, accessToken);

        int timeout = ssoConfiguration.getTimeout() / 2;
        //for test
//        timeout = 10;
        return new RpcAccessToken(accessToken, timeout, refreshToken,
                accessTokenContent.getUser());
    }


    /**
     * 检验获取AccessToken 的参数
     *
     * @param grantType
     * @param code
     * @param username
     * @param password
     * @return
     */
    public BooleanWithMsg validateParam(String grantType, String code, String username, String password) {
        if (GrantTypeEnum.AUTHORIZATION_CODE.name().equals(grantType)) {
            if (StringUtils.isEmpty(code)) {
                return BooleanWithMsg.fail("code不能为空");
            }
        } else if (GrantTypeEnum.PASSWORD.name().equals(grantType)) {
            if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
                return BooleanWithMsg.fail("username和password不能为空");
            }
        } else {
            return BooleanWithMsg.fail("授权方式不支持");
        }
        return BooleanWithMsg.success();
    }

}
